///////////////////////////////////////////////////////////
///////////////// Have an itch? Scratch it! ///////////////
///////////////////////// SCRATCH /////////////////////////
/////////////////////  A MUD  Server   ////////////////////
///////////////////// By: Jared Devall ////////////////////
/////////////////////      Thanks:     ////////////////////
/////////////////////  DIKU/Merc/ROM   ////////////////////
///////////////////// Aetas/Deus Gang  ////////////////////
/////////////////////       Beej       ////////////////////
///////////////////////////////////////////////////////////

#include "definitions.h"
#include <list>
#include "commands.h"
#include "commandTable.h"
#include "character.h"
#include "world.h"
#include "stringutil.h"
#include "split.h"
#include "timestamp.h"
#include "functions.h"
#include "object.h"
#include "help.h"

using namespace std;

// Command definitions for commands beginning with the letters G through M
// (Keep in alphabetical order... it's MUCH easier)
CmdGecho::CmdGecho() {
	SetName( "gecho" );
	SetLevel( GOD );
	SetEnabled( true );
	SetOmniset( false );
}

bool CmdGecho::Execute( Character* c, const string& args ) {
	std::string buf;
	std::string message;

	message << "{" << c->GetEchocolor() << args << "{x\n\r";

	World::Instance().Broadcast( message, ALL );
	return true;
}

CmdGet::CmdGet() {
	SetName( "get" );
	SetLevel( 1 );
	SetEnabled( true );
	SetOmniset( false );
}

bool CmdGet::Execute( Character* c, const string& args ) {
	std::string buf;
	std::string arg1, arg2;
	Object* target = 0;
	Object* container = 0;

	split( args, arg1, arg2 );

	if ( arg2.empty() ) {
		if ( arg1 == "all" ) { // Get all objects in room...
			std::string buf2;
			for ( std::list< Object* >::iterator it = c->getRoom()->getOlist().begin(); c->getRoom()->getOlist().size() != 0; it = c->getRoom()->getOlist().begin() ) {
				c->add( *it );
				c->getRoom()->Remove( *it );
				buf2 << "\n\r" << c->getName() << " gets " << (*it)->getShort() << "{x.";
				buf << "You get " << (*it)->getShort() << "{x.\n\r";
			}
			sendToRoom( c->getRoom(), buf2 );
			c->Send( buf );
			return true;
		} else { // Get an item from a container...
			if ( ( container = c->findObject( arg2 ) ) == NULL ) {
				c->Send( "You don't have " + arg2 + ".\n\r" );
				return false;
			}
			if ( ( target = container->getItem( arg1 ) ) == NULL ) {
				c->Send( arg1 + " isn't in " + container->getShort() + ".\n\r" );
				return false;
			}
		}
	} else { // Get a certain item from the room...
		for ( std::list< Object* >::iterator it = c->getRoom()->getOlist().begin(); it != c->getRoom()->getOlist().end(); it++ ) {
			if ( (*it)->getKeywords() == args || sub_str( args, (*it)->getKeywords() ) ) {
				c->getRoom()->Remove( *it );
				c->add( *it );
				buf << c->getName() << " picks up " << (*it)->getShort() << "{x.\n\r";
				sendToRoom( c->getRoom(), buf );
				buf = "You pick up ";
				buf << (*it)->getShort() << "{x.\n\r";
				c->Send( buf );
				return true;
			}
		}
	}

	c->Send( "error\n\r" );
	return false;
}

CmdGive::CmdGive() {
	SetName( "give" );	
	SetLevel( 1 );
	SetEnabled( true );
	SetOmniset( false );
}

bool CmdGive::Execute( Character* c, const string& args ) {
	std::string buf;
	std::string object_s;
	std::string target_s;
	Object* object = 0;
	Character* target = 0;
	std::list< Object* >::iterator o_it;
	std::list< Character* >::iterator c_it;

	split( args, object_s, target_s );

	if ( args.empty() || object_s.empty() || target_s.empty() ) {
		c->Send( "syntax: give <object> <target>\n\r" );
		return false;
	}

	for ( o_it = c->getInventory().begin(); o_it != c->getInventory().end(); o_it++ ) {
		std::string o_name = (*o_it)->getKeywords();
		if ( o_name == object_s || str_prefix( object_s, o_name ) )
			object = (*o_it);
	}

	if ( object == 0 ) {
		c->Send( "You don't have that object.\n\r" );
		return false;
	}

	for ( c_it = c->getRoom()->GetClist().begin(); c_it != c->getRoom()->GetClist().end(); c_it++ ) {
		std::string t_name = (*c_it)->getName();
		if ( t_name == target_s || str_prefix( target_s, t_name ) )
			target = (*c_it);
	}

	if ( target == 0 ) {
		c->Send( "They're not here.\n\r" );
		return false;
	}

	c->getInventory().remove( object );
	target->getInventory().push_front( object );

	buf << c->getName() << " gives " << object->getShort() << " to " << target->getName() << '.';
	sendToRoom( c->getRoom(), buf );
	buf = "You give ";
	buf << object->getShort() << " to " << target->getName() << ".\n\r";
	c->Send( buf );
	buf = c->getName();
	buf << " gives you " << object->getShort() << ".\n\r";
	target->Send( buf );

	return true;
}

CmdGoto::CmdGoto() {
	SetName( "goto" );
	SetLevel( BUILDER );
	SetEnabled( true );
	SetOmniset( false );
}

bool CmdGoto::Execute( Character* c, const string& args ) {
	Room* to = 0;
	Room* from = c->getRoom();
	Creature* target = 0;

	if ( args.empty() ) {
		c->Send( " >> \"goto\" transports you instantly to any room of your choosing.\n\r\n\rsyntax: goto <room vnum>\n\r        goto <character name>\n\r" );
		return false;
	}

	if ( isdigit( args[0] ) ) { // They gave a number...
		if ( ( to = World::Instance().FindRoom( atoi( args.c_str() ) ) ) == NULL ) {
			c->Send( "That room does not exist.\n\r" );
			return false;
		}
	} else if ( isalpha( args[0] ) ) { // They gave a name...
		if ( ( target = World::Instance().FindCharacter( args ) ) != NULL ) {
			to = target->getRoom();
		} else {
			if ( ( target = World::Instance().FindMob( args ) ) != NULL ) {
				to = target->getRoom();
			} else {
				c->Send( "That target could not be found.\n\r" );
				return false;
			}
		}
	} else {
		c->Send( " >> \"goto\" transports you instantly to any room of your choosing.\n\r\n\rsyntax: goto <room vnum>\n\r        goto <character name>\n\r" );
		return false;
	}

	if ( to == from ) {
		c->Send( "You're already there.\n\r" );
		return false;
	}

	from->Remove( c );
	if ( !c->getPoofout().empty() )
		sendToRoom( from, c->getPoofout() );
	if ( !c->getPoofin().empty() )
		sendToRoom( to, c->getPoofin() );
	c->setRoom( to->GetVnum() );
	to->Add( c );
	CmdLook command;
	command.Execute( c, "auto_look: goto" );

	return true;
}

CmdHelp::CmdHelp() {
	SetName( "help" );
	SetLevel( 1 );
	SetEnabled( true );
	SetOmniset( false );
}

bool CmdHelp::Execute( Character* c, const string& args ) {

	c->Send( World::Instance().getHelp( args ) );

	return true;
}

CmdInformation::CmdInformation() {
	SetName( "information" );
	SetLevel( BUILDER );
	SetEnabled( true );
	SetOmniset( false );
}

bool CmdInformation::Execute( Character* c, const string& args ) {
	std::string buf;
	int areas = 0;
	int rooms = 0;
	int exits = 0;
	int characters = 0;
	int dMobs = 0;
	int iMobs = 0;
	int dObjects = 0;
	int iObjects = 0;
	int resets = 0;
	int helps = 0;
	int boards = 0;
	int notes = 0;

	areas = Area::numAreas;
	rooms = Room::numRooms;
	dMobs = Mob::numMobs;
	iMobs = World::Instance().getMlist().size();
	dObjects  = Object::numObjects;
	iObjects = World::Instance().getOlist().size();
	resets = Reset::numResets;
	helps = Help::numHelps;
	boards = Board::numBoards;
	notes = Note::numNotes;

	// exits...
	for ( int x=0; x < Area::numAreas; x++ )
		for ( std::list< Room* >::iterator it = World::Instance().getArea( x )->getRooms().begin(); it != World::Instance().getArea( x )->getRooms().end(); it++ )
			for ( int z=0; z < 6; z++ )
				if ( (*it)->GetExit( z ).GetThere() )
					exits++;

	characters = World::Instance().getClist().size();

	buf << "\t{!WGeneral Information{x\n\r\n\r";
	buf << "Server version: " << World::Instance().getVersion() << "\n\r";
	buf << "Compiled: " << __TIME__ << " on " << __DATE__ << "\n\r"; // These macros may not be portable.
	buf << "Character index: " << World::Instance().getIndex() << "\n\r";
	buf << "Characters online: " << characters << "\n\r";
	buf << "Areas: " << areas << "\n\r";
	buf << "Rooms: " << rooms << "\n\r";
	buf << "Exits: " << exits << "\n\r";
	buf << "-Mobs: (" << dMobs << " types)\n\r";
	buf << "  Instances: " << iMobs << "\n\r";
	buf << "-Objects: (" << dObjects << " types)\n\r";
	buf << "  Instances: " << iObjects << "\n\r";
	buf << "Resets: " << resets << "\n\r";
	buf << "Help files: " << helps << "\n\r";
	buf << "Boards: " << boards << "\n\r";
	buf << "Notes: " << notes << "\n\r";

	c->Send( buf );

	return true;
}

CmdInventory::CmdInventory() {
	SetName( "inventory" );	
	SetLevel( 1 );
	SetEnabled( true );
	SetOmniset( false );
}

bool CmdInventory::Execute( Character* c, const string& args ) {
	std::string buf;

	if ( c->getInventory().size() == 0 ) {
		c->Send( "Your inventory is empty.\n\r" );
		return true;
	}

	buf << "You look at your inventory:\n\r";

	for ( std::list< Object* >::iterator it = c->getInventory().begin(); it != c->getInventory().end(); it++ )
		buf << "  " << (*it)->getShort() << "\n\r";

	c->Send( buf );
	return true;
}

CmdLook::CmdLook() {
	SetName( "look" );
	SetLevel( 1 );
	SetEnabled( true );
	SetOmniset( false );
}

bool CmdLook::Execute( Character* c, const std::string& args ) {
	std::string arg1, arg2;
	std::string buf;

	split( args, arg1, arg2 );

	if ( arg1.empty() ) {
		Room* room = c->getRoom();
		buf << "\n\r\n\r{g" << room->GetName() << "{x" << " [ {y" << room->GetVnum() << "{x ]\n\r";
		buf << room->GetDescolor() << room->getDesc() << "{x\n\rExits: {B[ {W";
		if ( room->NoExits() )
			buf << "{Rnone{x ";
		else
			for ( int x=0; x < 6; x++ )
				if ( room->GetExit( x ).GetThere() )
					buf << Exit::StringName( x ) << ' ';
		buf << "{B]{x\n\r";

		// Show Characters...
		for ( std::list< Character* >::iterator it = c->getRoom()->GetClist().begin(); it != c->getRoom()->GetClist().end(); it++ ) {
			if ( (*it) == c )
				continue;
			buf << "  " << (*it)->getName() << " is here mocking you.\n\r";
		}

		// Show mobs...
		for ( std::list< Mob* >::iterator it = room->getMlist().begin(); it != room->getMlist().end(); it++ )
			buf << "  " << (*it)->getLong() << "\n\r";

		// Show objects...
		for ( std::list< Object* >::iterator it = room->getOlist().begin(); it != room->getOlist().end(); it++ )
			buf << "  " << (*it)->getLong() << "\n\r";
	} else if ( arg1 == "in" ) {
		Object* o = 0;
		if ( ( o = c->findObject( arg2 ) ) == NULL ) {
			if ( o->getType() == CONTAINER ) {
				for ( std::list< Object* >::iterator it = o->getContents().begin(); it != o->getContents().end(); it++ )
					buf << '\t' << (*it)->getShort() << "\n\r";
			} else {
				c->Send( "That's not a container.\n\r" );
				return false;
			}
		} else {
			c->Send( "You don't have that container.\n\r" );
			return false;
		}
	}

	c->Send( buf );

	return true;
}

CmdMedit::CmdMedit() {
	SetName( "medit" );
	SetLevel( BUILDER );
	SetEnabled( true );
	SetOmniset( false );
}

bool CmdMedit::Execute( Character* c, const string& args ) {
	std::string buf;
	std::string syntax = "syntax: medit make <vnum>      creates a new mob\n\r        medit <vnum>           edits a mob <vnum>\n\r        medit remove <vnum>    removes a mob\n\r";
	std::string welcome = " Welcome to SEM (Symphony Edit Mode) for mobs!\n\r";
	std::string arg1, arg2;
	int vnum = 0;
	std::list< Area* >::iterator a_it;
	std::list< Mob* >::iterator m_it;

	split( args, arg1, arg2 );

	if ( arg1.empty() ) {
		c->Send( syntax );
		return false;
	} else if ( arg2.empty() ) {
		// They want to edit an existing mob...
		vnum = atoi( arg1.c_str() );
		if ( vnum <= 0 ) {
			// Validate vnum...
			c->Send( "That's an invalid vnum.\n\r" );
			return false;
		}
		// Find the mob...
		for ( a_it = World::Instance().getAreas().begin(); a_it != World::Instance().getAreas().end(); a_it++ ) {
			for ( m_it = (*a_it)->getMobs().begin(); m_it != (*a_it)->getMobs().end(); m_it++ ) {
				if ( (*m_it)->getVnum() == vnum && World::Instance().findParentArea( vnum )->getPermission( c->getId() ) ) {
					// Found the mob...
					c->SetStatus( MEDIT );
					c->setMob( *m_it );
					c->Send( welcome );
					return true;
				}
			}
		}
		// Mob doesn't exist...
		c->Send( "That mob doesn't exist.\n\r" );
		return false;
	} else if ( arg1 == "make" ) {
		// They want to create a new mob...
		vnum = atoi( arg2.c_str() );
		if ( vnum <= 0 ) {
			// Validate vnum...
			c->Send( "That's an invalid vnum.\n\r" );
			return false;
		}
		// Make sure the mob doesn't already exist...
		for ( a_it = World::Instance().getAreas().begin(); a_it != World::Instance().getAreas().end(); a_it++ ) {
			for ( m_it = (*a_it)->getMobs().begin(); m_it != (*a_it)->getMobs().end(); m_it++ ) {
				if ( (*m_it)->getVnum() == vnum ) {
					// Found the mob...
					c->Send( "That mob already exists!\n\r" );
					return false;
				}
			}
		}
		// Mob doesn't exist... let's see if they have permission to the area...
		if ( World::Instance().findParentArea( vnum ) == NULL ) {
			c->Send( "That vnum doesn't exist!\n\r" );
			return false;
		} else {
			if ( !World::Instance().findParentArea( vnum )->getPermission( c->getId() ) ) {
				c->Send( "You don't have permissions to that vnum!\n\r" );
				return false;
			} else {
				// They've got the permissions; We'll go ahead and create the mob now...
				Area* area = World::Instance().findParentArea( vnum );
				Mob* mob = area->addMob();
				mob->setVnum( vnum );
				c->SetStatus( MEDIT );
				c->setMob( mob );
				c->Send( welcome );
				return true;
			}
		}
	} else if ( arg1 == "remove" ) {
		// They want to delete a mob...
		vnum = atoi( arg2.c_str() );
		// Validate vnum...
		if ( vnum <= 0 ) {
			c->Send( "That's an invalid vnum.\n\r" );
			return false;
		}
		// Check permissions...
		if ( World::Instance().findParentArea( vnum ) == NULL ) {
			c->Send( "That vnum doesn't exist!\n\r" );
			return false;
		} else {
			if ( !World::Instance().findParentArea( vnum )->getPermission( c->getId() ) ) {
				c->Send( "You don't have permissions to that vnum!\n\r" );
				return false;
			}
		}
		// Find the mob...
		for ( a_it = World::Instance().getAreas().begin(); a_it != World::Instance().getAreas().end(); a_it++ ) {
			for ( m_it = (*a_it)->getMobs().begin(); m_it != (*a_it)->getMobs().end(); m_it++ ) {
				if ( (*m_it)->getVnum() == vnum ) {
					// Found the mob...
					(*a_it)->getMobs().remove( *m_it );
					c->Send( "Mob deleted.\n\r" );
					return true;
				}
			}
		}
		// Mob not found...
		c->Send( "That mob doesn't exist.\n\r" );
		return false;
	}

	// Something funky happened...
	c->Send( syntax );
	return false;
}

CmdMlist::CmdMlist() {
	SetName( "mlist" );
	SetLevel( BUILDER );
	SetEnabled( true );
	SetOmniset( false );
}

bool CmdMlist::Execute( Character* c, const string& args ) {
	std::string buf;
	Area* area = 0;
	std::string tmp;
	std::string search = args;
	bool found = false;

	if ( args.empty() ) {
		c->Send( "syntax: mlist all\n\r        mlist <string>    (lists all mobs with <string> in the name or short)\n\r" );
		return false;
	} else if ( args == "all" ) {
		// List all mobs...
		for ( int i=0; i < (int)World::Instance().getAreas().size(); i++ ) {
			area = World::Instance().getArea( i );
			for ( std::list< Mob* >::iterator it = area->getMobs().begin(); it != area->getMobs().end(); it++ )
				buf << "[ " << format( 5, (*it)->getVnum(), tmp, 'g' ) << "] " << (*it)->getShort() << "\n\r";
		}
		c->Send( buf );
		return true;
	} else {
		for ( int i=0; i < (int)World::Instance().getAreas().size(); i++ ) {
			// List only mobs whose names/short descriptions contain the given string...
			area = World::Instance().getArea( i );
			for ( std::list< Mob* >::iterator it = area->getMobs().begin(); it != area->getMobs().end(); it++ )
				if ( sub_str( search, (*it)->getName() ) ) {
					found = true;
					buf << "[ " << format( 5, (*it)->getVnum(), tmp, 'g' ) << "] " << (*it)->getShort() << "\n\r";
				}
		}

		if ( !found ) {
			buf = "No mobs were found with \"";
			buf << args << "\" in their name.\n\r";
		}

		c->Send( buf );
		return true;
	}

	c->Send( "{RERROR{x\n\r" );

	return false;
}

CmdMload::CmdMload() {
	SetName( "mload" );
	SetLevel( BUILDER );
	SetEnabled( true );
	SetOmniset( true );
}

bool CmdMload::Execute( Character* c, const string& args ) {
	std::string buf;
	std::list< Mob* >::iterator it;
	int vnum = atoi( args.c_str() );
	Mob* mob = 0;

	if ( !isdigit( args[0] ) ) {
		c->Send( "syntax: mload <vnum>\n\r" );
		return false;
	}

	if ( !args.empty() ) {
		for ( int i = 0; i < (int)World::Instance().getAreas().size(); i++ )
			for ( it = World::Instance().getArea( i )->getMobs().begin(); it != World::Instance().getArea( i )->getMobs().end(); it++ )
				if ( (*it)->getVnum() == vnum ) {
					mob = new Mob( *(*it) );
					mob->setRoom( c->getRoom()->GetVnum() );
					World::Instance().getMlist().push_back( mob );
					c->getRoom()->Add( mob );
					c->setMob( mob );
					buf << c->getName() << " has created " << mob->getShort();
					sendToRoom( c->getRoom(), buf );
					buf = "You've loaded mob ";
					buf << c->getMob()->getVnum() << " (\"" << c->getMob()->getShort() << "\")\n\r";
					c->Send( buf );
					return true;
				}
	}

	c->Send( "That mob wasn't found.\n\r" );

	return false;
}
